package com.example.coroutines_test.customize

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_DOWN
import android.view.MotionEvent.ACTION_MOVE
import android.view.MotionEvent.ACTION_UP
import android.view.View
import android.view.View.MeasureSpec.AT_MOST
import android.view.ViewGroup
import android.widget.Scroller
import kotlin.math.abs
import kotlin.math.max

class HorizonViewGroup(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : ViewGroup(context, attrs, defStyleAttr, defStyleRes) {

    private var lastInterceptedX = 0f
    private var lastInterceptedY = 0f


    private var lastTouchX = 0f
    private var lastTouchY = 0f

    private var startTouchX = 0f
    private var startTouchY = 0f

    /**
     * 我们在用scollTo/scollBy方法进行滑动时，这个过程是瞬间完成的，所以用户体验不大好。
     * 这里我们可以使用 Scroller 来实现有过渡效果的滑动，
     * 这个过程不是瞬间完成的，而是在一定的时间间隔内完成的。
     * Scroller本身是不能实现View的滑动的，它需要与View的computeScroll()方法配合才能实现弹性滑动的效果。
     * 在这里我们实现 HorizonViewGroup 平滑地向右移动。首先我们要初始化Scroller
     * */
    val scroller = Scroller(context)

    constructor(context: Context) : this(context, null)

    constructor(
        context: Context,
        attrs: AttributeSet?
    ) : this(context, attrs, 0)

    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int
    ) : this(context, attrs, defStyleAttr, 0)


    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

        val widthSpecMode = MeasureSpec.getMode(widthMeasureSpec)
        val widthSpecSize = MeasureSpec.getSize(widthMeasureSpec)

        val heightSpecMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightSpecSize = MeasureSpec.getSize(heightMeasureSpec)

        // 测量子元素的宽高
        // 因为 ViewGroup 没有实现 onMeasure() 方法
        // 而 ViewGroup 的核心 measure 逻辑在 measureChildren() 中
        // 所以继承 ViewGroup 的子类需要把 onMeasure() 和 measureChildren() 给桥接起来
        // 这里要先测量子元素，然后再拿到子元素测量过后的宽高进行父组件（本组件）的计算
        measureChildren(widthMeasureSpec, heightMeasureSpec)

        if (childCount == 0) {
            // 没有子元素
            setMeasuredDimension(0, 0)
            return
        }
        /*
            注意在后续使用      measuredWidth 和 measuredHeight 的过程中
            就不再需要额外加上 padding 和 margin 了，因为已经被包含在 measuredWidth 和 measuredHeight 中了
        * */
        var totalWidth = 0
        var maxHeight = 0
        for (i in 0 until childCount) {
            val childView = getChildAt(i)
            if (childView.visibility == GONE) {
                continue
            }
            totalWidth += childView.measuredWidth //+ childView.paddingLeft + childView.paddingRight + childView.marginLeft + childView.marginRight
            maxHeight = max(
                maxHeight,
                childView.measuredHeight //+ childView.paddingTop + childView.paddingBottom + childView.marginTop + childView.marginBottom
            )
        }

        if (widthSpecMode == AT_MOST && heightSpecMode == AT_MOST) {
            // 子元素自定义自己的宽高

            setMeasuredDimension(totalWidth, maxHeight)
        } else if (widthSpecMode == AT_MOST) {
            // 子元素自定义自己的宽
            setMeasuredDimension(totalWidth, heightSpecSize)
        } else if (heightSpecMode == AT_MOST) {
            // 子元素自定义自己的高
            setMeasuredDimension(widthSpecSize, maxHeight)
        } else {
            // 使用父容器指定的宽高
            setMeasuredDimension(widthSpecSize, heightSpecSize)
        }


    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {

        var leftOffset = 0
        for (i in 0 until childCount) {
            val childView = getChildAt(i)

            //leftOffset+=childView.marginLeft + childView.paddingLeft

            childView.layout(
                leftOffset,
                0,//childView.marginTop + childView.paddingTop,
                leftOffset + childView.measuredWidth,
                childView.measuredHeight //+childView.marginTop + childView.paddingTop
            )
            leftOffset += childView.measuredWidth //+ childView.paddingRight + childView.marginRight
        }
    }

    /**
     * 返回是否要拦截这个触摸事件
     */
    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {

        var intercept = false

        val x = ev?.x ?: 0f
        val y = ev?.y ?: 0f
        when (ev?.action) {
            ACTION_DOWN -> {
                lastInterceptedX = x
                lastInterceptedY = y
            }

            ACTION_MOVE -> {
                val dx = x - lastInterceptedX
                val dy = y - lastInterceptedY
                if (abs(dx) > abs(dy)) {
                    intercept = true
                }
            }

            ACTION_UP -> {

            }

            else -> {

            }
        }


        return intercept
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        val x = event?.x ?: 0f
        val y = event?.y ?: 0f
        val dx = x - lastTouchX
        when (event?.action) {
            ACTION_DOWN -> {
                startTouchX = x
                startTouchY = y
            }

            ACTION_MOVE -> {
                scrollBy(-dx.toInt(), 0)
            }

            ACTION_UP -> {
                // 把这里注释掉也能正常使用
               // smoothScrollTo(-(x - startTouchX).toInt(), 0)
            }
        }



        lastTouchX = x

        lastTouchY = y
        return true
    }

    /**
     * 系统会在绘制View的时候在draw()方法中调用该方法。
     * 在这个方法中，我们调用父类的scrollTo()方法并通过Scroller来不断获取当前的滚动值，
     * 每滑动一小段距离我们就调用invalidate()方法不断地进行重绘，
     * 重绘就会调用computeScroll()方法，
     * 这样我们通过不断地移动一个小的距离并连贯起来就实现了平滑移动的效果。
     * */
    override fun computeScroll() {
        super.computeScroll()
        if (scroller.computeScrollOffset()) {
            scrollTo(scroller.currX, scroller.currY)

            invalidate()
        }
    }

    fun smoothScrollTo(destX: Int, destY: Int) {

        val dx = destX - scrollX
        scroller.startScroll(scrollX, 0, dx, 0)
    }
}